﻿#include "../control.h"
#include <algorithm>

namespace swui
{
	template<typename T, typename T2>
	static void _eventSende(ctlRoot* ctl, T& value, T2* info)
	{
		bool isNextHandler = true;
		value.isCalling = true;
		for(auto& v : value.contain)
		{
			//检测当前事件处理函数是否可用（可能被移除但没有从容器擦除）
			if(v.enable == false) continue;
			T2 ke = *info;
			ke.userParam = v.param;
			ke.nextHandler = &isNextHandler;
			(v.value)(ctl, &ke);
			if(!isNextHandler || ctl->isCreated() == false) break;
		}
		//事件处理中添加的事件处理函数会在 tempContain 中
		//为了防止迭代器失效
		if(ctl->isCreated() == false) return;
		while(value.tempContain.empty() == false)
		{
			std::size_t size = value.contain.size();
			value.contain.insert(value.contain.end(), value.tempContain.begin(), value.tempContain.end());
			value.tempContain.clear();
			for(auto beg = value.contain.begin() + size, end = value.contain.end(); beg != end; ++beg)
			{
				if(beg->enable == false) continue;
				T2 ke = *info;
				ke.nextHandler = &isNextHandler;
				ke.userParam = beg->param;
				(beg->value)(ctl, &ke);
				if(!isNextHandler || ctl->isCreated() == false) break;
			}
		}
		//删除那些不可用的事件处理函数
		if(ctl->isCreated() == false) return;
		for(auto beg = value.contain.begin(); beg != value.contain.end(); ++beg)
		{
			if(beg->enable == false)
			{
				beg = value.contain.erase(beg);
				if(beg == value.contain.end()) break;
			}
		}
		value.isCalling = false;
	}

	//基本键盘事件
	void ctlRoot::_key_event_(ctlKeyEvent* info)
	{
		if(_key_method.count(info->eventType))
			_eventSende(this, _key_method[info->eventType], info);
	}
	//基本鼠标事件
	void ctlRoot::_mouse_event_(ctlMouseEvent* info)
	{
		auto eventModelSender = [this, info](ctlMouseEventModel model)
		{
			if(info->eventType == ctlMouseEventType::leave ||
				info->eventType == ctlMouseEventType::enter)
				return;
			if(getMouseEventModel() == model && getParent())
			{
				ctlMouseEvent me = *info;
				me.eventModel = model;
				me.mousePos.x += getLeft();
				me.mousePos.y += getTop();
				me.eventFrom = this;
				getParent()->_mouse_event_(&me);
				//非 cut 模型不会调用 ctlRoot 事件处理函数
				getParent()->ctlRoot::_mouse_event_(&me);
			}
		};
		eventModelSender(ctlMouseEventModel::capture);
		if(info->eventType == ctlMouseEventType::leftDown
			&& info->eventModel == ctlMouseEventModel::cut)
			setFocus();
		if(_mouse_method.count(info->eventType))
			_eventSende(this, _mouse_method[info->eventType], info);
		eventModelSender(ctlMouseEventModel::bubble);
	}
	//基本位置事件
	void ctlRoot::_position_event_(ctlPositionEvent* info)
	{
		if(info->eventType == ctlPositionEventType::move
			|| info->eventType == ctlPositionEventType::size)
		{
			//改变位置或大小，有可能影响到同级别的 relative 的 move/size 定位
			//如当对齐方式是 rightBottom 时，大小就会影响到
			if(getParent() != nullptr)
			{
				ctlRoot *parent = getParent();
				for(const auto& node : _size_related)
				{
					if(node.first->getParent() == parent)
						node.first->_size_change();
				}
				for(const auto& node : _move_related)
				{
					if(node.first->getParent() == parent)
						node.first->_move_change();
				}
			}
		}
		if(info->eventType == ctlPositionEventType::move)
		{
			_size_change();
			//修正子元素定位
			for(ctlRoot* node : _eventTrans.getChildEnumerator())
				node->_move_change();
		}
		if(info->eventType == ctlPositionEventType::size)
		{
			_move_change();
			for(ctlRoot* node : _eventTrans.getChildEnumerator())
			{
				node->_size_change();
				//如果大小改变，那么有可能影响到 percent 的 move 定位
				//也可能影响到 relative 的 move 定位（父子元素）
				node->_move_change();
			}
		}
		if(_position_method.count(info->eventType))
			_eventSende(this, _position_method[info->eventType], info);
	}
	//基本通告事件
	void ctlRoot::_notify_event_(ctlNotifyEvent* info)
	{
		if(_notify_method.count(info->eventType))
			_eventSende(this, _notify_method[info->eventType], info);
	}

	void ctlRoot::addEvent(ctlMouseEventType eventType, ptrMouseEvent method, ctlMouseEvent::paramType param)
	{
		auto& value = _mouse_method[eventType];
		if(value.isCalling) value.tempContain.push_back(
			_ctlInterior::eventContain<ptrMouseEvent, ctlMouseEvent::paramType>::data(method, param));
		else  value.contain.push_back(
			_ctlInterior::eventContain<ptrMouseEvent, ctlMouseEvent::paramType>::data(method, param));
	}
	void ctlRoot::addEvent(ctlKeyEventType eventType, ptrKeyEvent method, ctlKeyEvent::paramType param)
	{
		auto& value = _key_method[eventType];
		if(value.isCalling) value.tempContain.push_back(
			_ctlInterior::eventContain<ptrKeyEvent, ctlKeyEvent::paramType>::data(method, param));
		else value.contain.push_back(
			_ctlInterior::eventContain<ptrKeyEvent, ctlKeyEvent::paramType>::data(method, param));
	}
	void ctlRoot::addEvent(ctlPositionEventType eventType, ptrPositionEvent method, ctlPositionEvent::paramType param)
	{
		auto& value = _position_method[eventType];
		if(value.isCalling) value.tempContain.push_back(
			_ctlInterior::eventContain<ptrPositionEvent, ctlPositionEvent::paramType>::data(method, param));
		else value.contain.push_back(
			_ctlInterior::eventContain<ptrPositionEvent, ctlPositionEvent::paramType>::data(method, param));
	}
	void ctlRoot::addEvent(ctlNotifyEventType eventType, ptrNotifyEvent method, ctlNotifyEvent::paramType param)
	{
		auto& value = _notify_method[eventType];
		if(value.isCalling) value.tempContain.push_back(
			_ctlInterior::eventContain<ptrNotifyEvent, ctlNotifyEvent::paramType>::data(method, param));
		else value.contain.push_back(
			_ctlInterior::eventContain<ptrNotifyEvent, ctlNotifyEvent::paramType>::data(method, param));
	}
	template<typename ParamType, typename T, typename Method> 
	static bool _removeEvent(T& value, Method method, ParamType param, bool isParam)
	{
		auto result = std::find(value.contain.begin(), value.contain.end(), method);
		if(isParam) while(result != value.contain.end() && result->param != param)
				result = std::find(result + 1, value.contain.end(), method);
		while(result != value.contain.end() && !result->enable)
				result = std::find(result + 1, value.contain.end(), method);
		if(result == value.contain.end()) 
		{
			if(!value.isCalling) return false;
			result = std::find(value.tempContain.begin(), value.tempContain.end(), method);
			if(isParam) while(result != value.tempContain.end() && result->param != param)
				result = std::find(result + 1, value.tempContain.end(), method);
			while(result != value.tempContain.end() && !result->enable)
					result = std::find(result + 1, value.tempContain.end(), method);
			if(result == value.tempContain.end()) return false;
//			if(!result->enable) return false;
			result->enable = false;
			return true;
		}
//		if(!result->enable) return false;
		if(value.isCalling) result->enable = false;
		else value.contain.erase(result);
		return true;
	}
	//鼠标事件移除
	bool ctlRoot::removeEvent(ctlMouseEventType eventType, ptrMouseEvent method)
	{
		if(_mouse_method.count(eventType) == 0) return false;
		return _removeEvent<ctlMouseEvent::paramType>(_mouse_method[eventType], method, 0, false);
	}
	//鼠标事件移除
	bool ctlRoot::removeEvent(ctlMouseEventType eventType, ptrMouseEvent method, ctlMouseEvent::paramType param)
	{
		if(_mouse_method.count(eventType) == 0) return false;
		return _removeEvent<ctlMouseEvent::paramType>(_mouse_method[eventType], method, param, true);
	}
	//键盘事件移除
	bool ctlRoot::removeEvent(ctlKeyEventType eventType, ptrKeyEvent method)
	{
		if(_key_method.count(eventType) == 0) return false;
		return _removeEvent<ctlKeyEvent::paramType>(_key_method[eventType], method, 0, false);
	}
	//键盘事件移除
	bool ctlRoot::removeEvent(ctlKeyEventType eventType, ptrKeyEvent method, ctlKeyEvent::paramType param)
	{
		if(_key_method.count(eventType) == 0) return false;
		return _removeEvent<ctlKeyEvent::paramType>(_key_method[eventType], method, param, true);
	}
	//位置事件移除
	bool ctlRoot::removeEvent(ctlPositionEventType eventType, ptrPositionEvent method)
	{
		if(_position_method.count(eventType) == 0) return false;
		return _removeEvent<ctlPositionEvent::paramType>(_position_method[eventType], method, 0, false);
	}
	//位置事件移除
	bool ctlRoot::removeEvent(ctlPositionEventType eventType, ptrPositionEvent method, ctlPositionEvent::paramType param)
	{
		if(_position_method.count(eventType) == 0) return false;
		return _removeEvent<ctlPositionEvent::paramType>(_position_method[eventType], method, param, true);
	}	
	bool ctlRoot::removeEvent(ctlNotifyEventType eventType, ptrNotifyEvent method)
	{
		if(_notify_method.count(eventType) == 0) return false;
		return _removeEvent<ctlNotifyEvent::paramType>(_notify_method[eventType], method, 0, false);
	}
	bool ctlRoot::removeEvent(ctlNotifyEventType eventType, ptrNotifyEvent method, ctlNotifyEvent::paramType param)
	{
		if(_notify_method.count(eventType) == 0) return false;
		return _removeEvent<ctlNotifyEvent::paramType>(_notify_method[eventType], method, param, true);
	}
}